{
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "# @title ###### Licensed to the Apache Software Foundation (ASF), Version 2.0 (the \"License\")\n",
        "\n",
        "# Licensed to the Apache Software Foundation (ASF) under one\n",
        "# or more contributor license agreements. See the NOTICE file\n",
        "# distributed with this work for additional information\n",
        "# regarding copyright ownership. The ASF licenses this file\n",
        "# to you under the Apache License, Version 2.0 (the\n",
        "# \"License\"); you may not use this file except in compliance\n",
        "# with the License. You may obtain a copy of the License at\n",
        "#\n",
        "#   http://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing,\n",
        "# software distributed under the License is distributed on an\n",
        "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n",
        "# KIND, either express or implied. See the License for the\n",
        "# specific language governing permissions and limitations\n",
        "# under the License"
      ],
      "metadata": {
        "id": "NsNImDL8TGM1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Image Processing using Apache Beam\n",
        "\n",
        "<table align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.sandbox.google.com/github/apache/beam/blob/master/examples/notebooks/beam-ml/image_processing_tensorflow.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/apache/beam/blob/master/examples/notebooks/beam-ml/image_processing_tensorflow.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>\n",
        "\n"
      ],
      "metadata": {
        "id": "SwN0Rj4cJSg5"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Image Processing is a machine learning technique to read, analyze and extract meaningful information from images. It involves multiple steps such as applying various preprocessing functions, getting predictions from a model, storing the predictions in a useful format, etc. Apache Beam is a suitable tool to handle these tasks and build a structured workflow. This notebook demonstrates the use of Apache Beam in image processing and performs the following:\n",
        "* Import and preprocess the CIFAR-10 dataset\n",
        "* Train a TensorFlow model to classify images\n",
        "* Store the model in Google Cloud and create a model handler\n",
        "* Build a Beam pipeline to:\n",
        " 1. Create a [PCollection]('https://beam.apache.org/documentation/programming-guide/#pcollections') of input images\n",
        " 2. Perform preprocessing [transforms]('https://beam.apache.org/documentation/programming-guide/#transforms')\n",
        " 3. RunInference to get predictions from the previously trained model\n",
        " 4. Store the results\n",
        "\n",
        "For more information on using Apache Beam for machine learning, have a look at [AI/ML Pipelines using Beam]('https://beam.apache.org/documentation/ml/overview/')."
      ],
      "metadata": {
        "id": "yxLoBQxocAOv"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OSZrRmHl9NQY"
      },
      "source": [
        "## Installing Apache Beam"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MO7iNmvkBdA5",
        "outputId": "6c76e29d-3c70-4c3e-aca2-7cc1dcd167a1"
      },
      "outputs": [],
      "source": [
        "!pip install apache-beam[interactive] --quiet"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "45mf7oHu9XbI"
      },
      "source": [
        "## Importing necessary libraries\n",
        "Here is a brief overview of the uses of each library imported:\n",
        "* **NumPy**: Multidimensional numpy arrays are used to store images, and the library also allows performing various operations on them.\n",
        "* **Matplotlib**: Displays images stored in numpy array format.\n",
        "* **TensorFlow**: Trains a machine learning model.\n",
        "* **TFModelHandlerNumpy**: Defines the configuration used to load/use the model that we train. We use `TFModelHandlerNumpy` because the model was trained with TensorFlow and takes numpy arrays as input.\n",
        "* **RunInference**:  Loads the model and obtains predictions as part of the Apache Beam pipeline. For more information, see [docs on prediction and inference](https://beam.apache.org/documentation/ml/inference-overview/).\n",
        "* **Apache Beam**: Builds a pipeline for Image Processing."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "z5_PUeZgOygU"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import tensorflow as tf\n",
        "from apache_beam.ml.inference.tensorflow_inference import TFModelHandlerNumpy\n",
        "from apache_beam.ml.inference.base import RunInference\n",
        "import apache_beam as beam"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "x3tSAqP7R2rZ"
      },
      "source": [
        "## CIFAR-10 Dataset\n",
        "CIFAR-10 is a popular dataset used for multiclass object classification.\n",
        "It has 60,000 images of the following 10 categories:\n",
        "\n",
        "* airplane\n",
        "* automobile\n",
        "* bird\n",
        "* cat\n",
        "* deer\n",
        "* dog\n",
        "* frog\n",
        "* horse\n",
        "* ship\n",
        "* truck\n",
        "\n",
        "The dataset can be directly imported from the TensorFlow library."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MqylmjBhPCOW",
        "outputId": "9d9f5854-80f2-4a4f-a52b-2b81d6295639"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz\n",
            "170498071/170498071 [==============================] - 4s 0us/step\n"
          ]
        }
      ],
      "source": [
        "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "x_test.shape"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "pfzkgryZUV8P",
        "outputId": "79bc798f-f93b-4d7b-8783-c5defa6a2322"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(10000, 32, 32, 3)"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "The labels in y_train and y_test are numeric, with each number representing a class. The labels list defined below contains the various classes, and their positions in the list represent the corresponding number used to refer to them."
      ],
      "metadata": {
        "id": "6hEHIHPsVxw4"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3uImFIBXv0My"
      },
      "outputs": [],
      "source": [
        "labels = ['Airplane', 'Automobile', 'Bird', 'Cat', 'Deer', 'Dog', 'Frog', 'Horse','Ship', 'Truck']"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 447
        },
        "id": "zeE81PNOcGfZ",
        "outputId": "d2a08cb5-4fdc-47af-c2b2-5602e7600f09"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<matplotlib.image.AxesImage at 0x7f441be49840>"
            ]
          },
          "metadata": {},
          "execution_count": 6
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "plt.imshow(x_train[800])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4arvJDYwfsAj",
        "outputId": "a355a4e2-c1a7-461e-bff9-059daaa6a9f7"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(32, 32, 3)"
            ]
          },
          "metadata": {},
          "execution_count": 7
        }
      ],
      "source": [
        "x_train[0].shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ndeZ_RH32Upu"
      },
      "source": [
        "(32, 32, 3) represents an image of size 32x32 in the RGB scale"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Preprocessing"
      ],
      "metadata": {
        "id": "L2pg1uxSXPHn"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Standardization** is the process of transforming the pixel values of an image to have zero mean and unit variance. This brings the pixel values to a similar scale and makes them easier to work with."
      ],
      "metadata": {
        "id": "Hwwm-EHhW0rC"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZlInmab9MD-N"
      },
      "outputs": [],
      "source": [
        "x_train = x_train/255.0"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Normalization** is the process of scaling the pixel values to a specified range, typically between 0 and 1. This improves the consistency of images."
      ],
      "metadata": {
        "id": "6GFdU-HZWztg"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 447
        },
        "id": "TLmsgV9_Wij5",
        "outputId": "03fb00c5-efb9-421c-ef55-bbd36679dfbe"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<matplotlib.image.AxesImage at 0x7f4412adeb30>"
            ]
          },
          "metadata": {},
          "execution_count": 9
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "x_train = (x_train - np.min(x_train)) / (np.max(x_train) - np.min(x_train))\n",
        "plt.imshow(x_train[800])"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Grayscale Conversion** refers to the conversion of a colored image in RGB scale into a grayscale image. It represents the pixel intensities without considering colors, which makes calculations easier."
      ],
      "metadata": {
        "id": "bfgy0Z_gX_lH"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "P2oPvZkbfEPo"
      },
      "outputs": [],
      "source": [
        "grayscale = []\n",
        "for i in x_train:\n",
        "  grayImage = 0.07 * i[:,:,2] + 0.72 * i[:,:,1] + 0.21 * i[:,:,0]\n",
        "  grayscale.append(grayImage)\n",
        "x_train_gray = np.asarray(grayscale)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jzQ2Zulg99NU"
      },
      "source": [
        "## Defining DoFns for Image Preprocessing\n",
        "\n",
        "[DoFn](https://beam.apache.org/releases/typedoc/current/interfaces/transforms_pardo.DoFn) stands for \"Do Function\". In Apache Beam, it is a set of operations that can be applied to individual elements of a PCollection (a collection of data). It is similar to a function in Python, except that it is used in Beam Pipelines to apply various transformations. DoFns can be used in various Apache Beam transforms, such as ParDo, Map, Filter, and FlatMap."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cqm-m0cONsZS"
      },
      "outputs": [],
      "source": [
        "class StandardizeImage(beam.DoFn):\n",
        "  def process(self, element: np.ndarray):\n",
        "    element = element/255.0\n",
        "    return [element]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mZhFCgPxPEwm"
      },
      "outputs": [],
      "source": [
        "class NormalizeImage(beam.DoFn):\n",
        "  def process(self, element: np.ndarray):\n",
        "    element = (element-element.min())/(element.max()-element.min())\n",
        "    return [element]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gv23KPt5NyXT"
      },
      "outputs": [],
      "source": [
        "class GrayscaleImage(beam.DoFn):\n",
        "  def process(self, element: np.ndarray):\n",
        "    element = 0.07 * element[:,:,2] + 0.72 * element[:,:,1] + 0.21 * element[:,:,0]\n",
        "    return [element]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8gz7_SvN-P2L"
      },
      "source": [
        "## Training a Convolutional Neural Network\n",
        "\n",
        "A Convolutional Neural Network (CNN) is one of the most popular model types for image processing. Here is a brief description of the convolutional layers used in the model.\n",
        "* **Reshape**: Changes the shape of the input data to the desired size.\n",
        "The CIFAR-10 images are of 32x32 pixels in grayscale. We will train our model using these images and thus, all images fed into the model need to be reshaped to the required size, that is (32,32,1).\n",
        "* **Conv2D**: Applies a set of filters to extract features from the input image, producing a feature map as the output.\n",
        "This layer is used as it is an essential component of a CNN, and does the major task of finding patterns in images.\n",
        "* **MaxPooling2D**: Reduces the spatial dimensions of the input while retaining the most prominent features.\n",
        "We use this layer to downsample the images and preserve only the important features.\n",
        "* **Flatten**: Flattens the input data or feature maps into a 1-dimensional vector.\n",
        "The input images are 2-dimensional. However in the end we require our results in a 1-D array. Flatten layer is used for this.\n",
        "* **Dense**: Connects every neuron in the current layer to every neuron in the subsequent layer.\n",
        "The CIFAR-10 dataset contains images belonging to 10 different classes. This is why the last dense layer gives 10 outputs, where each output corresponds to the probability of an image belonging to one of the 10 classes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "chYD9y7cH4Td",
        "outputId": "b91eed4e-c383-40b8-96dc-8cf330d11a09"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Model: \"sequential\"\n",
            "_________________________________________________________________\n",
            " Layer (type)                Output Shape              Param #   \n",
            "=================================================================\n",
            " reshape (Reshape)           (None, 32, 32, 1)         0         \n",
            "                                                                 \n",
            " conv2d (Conv2D)             (None, 30, 30, 32)        320       \n",
            "                                                                 \n",
            " max_pooling2d (MaxPooling2D  (None, 15, 15, 32)       0         \n",
            " )                                                               \n",
            "                                                                 \n",
            " conv2d_1 (Conv2D)           (None, 13, 13, 64)        18496     \n",
            "                                                                 \n",
            " max_pooling2d_1 (MaxPooling  (None, 6, 6, 64)         0         \n",
            " 2D)                                                             \n",
            "                                                                 \n",
            " conv2d_2 (Conv2D)           (None, 4, 4, 64)          36928     \n",
            "                                                                 \n",
            " flatten (Flatten)           (None, 1024)              0         \n",
            "                                                                 \n",
            " dense (Dense)               (None, 64)                65600     \n",
            "                                                                 \n",
            " dense_1 (Dense)             (None, 10)                650       \n",
            "                                                                 \n",
            "=================================================================\n",
            "Total params: 121,994\n",
            "Trainable params: 121,994\n",
            "Non-trainable params: 0\n",
            "_________________________________________________________________\n"
          ]
        }
      ],
      "source": [
        "def create_model():\n",
        "  model = tf.keras.Sequential([\n",
        "    tf.keras.layers.Reshape((32,32,1),input_shape=x_train_gray.shape[1:]),\n",
        "    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 1)),\n",
        "    tf.keras.layers.MaxPooling2D((2, 2)),\n",
        "    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n",
        "    tf.keras.layers.MaxPooling2D((2, 2)),\n",
        "    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n",
        "    tf.keras.layers.Flatten(),\n",
        "    tf.keras.layers.Dense(64, activation='relu'),\n",
        "    tf.keras.layers.Dense(10)\n",
        "  ])\n",
        "  model.compile(optimizer='adam',\n",
        "              loss='sparse_categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "  return model\n",
        "\n",
        "model = create_model()\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "The input shape is changed to (32,32,1) as our input images are of 32 x 32 pixels and 1 represents grayscale. In the final dense layer, there are 10 outputs as there are 10 possible classes in the CIFAR-10 dataset."
      ],
      "metadata": {
        "id": "UpO090vNbHir"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3D1SdCC94DQi"
      },
      "source": [
        "## Fitting the model"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "KuMP46UVXkof",
        "outputId": "5340424c-7a33-45c9-b773-3ff625c65290"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Epoch 1/10\n",
            "1563/1563 [==============================] - 87s 55ms/step - loss: 1.6511 - accuracy: 0.4054\n",
            "Epoch 2/10\n",
            "1563/1563 [==============================] - 84s 54ms/step - loss: 1.2737 - accuracy: 0.5540\n",
            "Epoch 3/10\n",
            "1563/1563 [==============================] - 80s 51ms/step - loss: 1.1204 - accuracy: 0.6095\n",
            "Epoch 4/10\n",
            "1563/1563 [==============================] - 79s 51ms/step - loss: 1.0184 - accuracy: 0.6461\n",
            "Epoch 5/10\n",
            "1563/1563 [==============================] - 80s 51ms/step - loss: 0.9430 - accuracy: 0.6724\n",
            "Epoch 6/10\n",
            "1563/1563 [==============================] - 81s 52ms/step - loss: 0.8810 - accuracy: 0.6946\n",
            "Epoch 7/10\n",
            "1563/1563 [==============================] - 80s 51ms/step - loss: 0.8299 - accuracy: 0.7135\n",
            "Epoch 8/10\n",
            "1563/1563 [==============================] - 80s 51ms/step - loss: 0.7904 - accuracy: 0.7248\n",
            "Epoch 9/10\n",
            "1563/1563 [==============================] - 80s 51ms/step - loss: 0.7504 - accuracy: 0.7385\n",
            "Epoch 10/10\n",
            "1563/1563 [==============================] - 84s 54ms/step - loss: 0.7150 - accuracy: 0.7498\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<keras.callbacks.History at 0x7f4412ead3c0>"
            ]
          },
          "metadata": {},
          "execution_count": 22
        }
      ],
      "source": [
        "model.fit(x_train_gray, y_train, epochs=10)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3obkK5fQ4KG6"
      },
      "source": [
        "## Authenticating from Google Cloud\n",
        "\n",
        "We need to store our trained model in Google Cloud. For running inferences, we will load our model from cloud into the notebook using a Model Handler."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Jj_NX568mhsE"
      },
      "outputs": [],
      "source": [
        "from google.colab import auth\n",
        "auth.authenticate_user()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7jn7HYPi4hH9"
      },
      "source": [
        "Saving the trained model in a Google Cloud Storage bucket"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CDqLPkzLfrt1"
      },
      "outputs": [],
      "source": [
        "save_model_dir = '' # Add the link to you GCS bucket here\n",
        "model.save(save_model_dir)"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "A model handler is used to save, load and manage trained ML models. Here we used TFModelHandlerNumpy as our input images are in the form of numpy arrays."
      ],
      "metadata": {
        "id": "ilYg15uOcZSY"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "model_handler = TFModelHandlerNumpy(save_model_dir)"
      ],
      "metadata": {
        "id": "RqefS6I_c1kc"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Saving predictions\n",
        "RunInference returns the predictions for each class. In the below DoFn, the maximum predicion is selected (which refers to the class the input image most probably belongs to) and is stored in a list of predictions."
      ],
      "metadata": {
        "id": "4vJZEXFOboUi"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9T_YW19P46dW"
      },
      "outputs": [],
      "source": [
        "from tensorflow.python.ops.numpy_ops import np_config\n",
        "np_config.enable_numpy_behavior()\n",
        "predictions = []\n",
        "class SavePredictions(beam.DoFn):\n",
        "  def process(self, element, *args, **kwargs):\n",
        "    list_of_predictions = element.inference.tolist()\n",
        "    highest_prediction = max(list_of_predictions)\n",
        "    ans = labels[list_of_predictions.index(highest_prediction)]\n",
        "    predictions.append(ans)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c5x1HD_V4-RS"
      },
      "source": [
        "## Building a Beam Pipeline\n",
        "\n",
        "A Pipeline represents the workflow of a series of computations. Here we are performing the following tasks in our pipeline:\n",
        "* Creating a PCollection of the data on which we need to run inference\n",
        "* Appying the Image Preprocessing DoFns we defined earlier <br>\n",
        " These include:\n",
        " 1. Standardization\n",
        " 2. Normalization\n",
        " 3. Converting to grayscale\n",
        "* Running Inference by using the trained model stored in Google Cloud.\n",
        "* Displaying the output of the model"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KxYdNXefTFcD"
      },
      "outputs": [],
      "source": [
        "with beam.Pipeline() as p:\n",
        "    _ = (p | beam.Create(x_test)\n",
        "           | beam.ParDo(StandardizeImage())\n",
        "           | beam.ParDo(NormalizeImage())\n",
        "           | beam.ParDo(GrayscaleImage())\n",
        "           | RunInference(model_handler)\n",
        "           | beam.ParDo(SavePredictions())\n",
        "        )"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "So we got our predictions! Let us verify one of them."
      ],
      "metadata": {
        "id": "yCADuo_yk1sK"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "index = 5000\n",
        "#You can change this index value to see and verify any image\n",
        "predictions[index]"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "Zj2p_V2qmYMM",
        "outputId": "f45c4f9f-3fa2-49f9-8471-602eea8faf68"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Horse'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 40
        }
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 447
        },
        "id": "kd5fXda1yr6G",
        "outputId": "51589888-2986-4d99-a6a3-c89ff24ebd9e"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<matplotlib.image.AxesImage at 0x7f4418bb7ac0>"
            ]
          },
          "metadata": {},
          "execution_count": 30
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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giDQgw8+qG9961u65pprJEk//vGPVV5erqeeeko33HDD6a0WADBiDOhrQA0NDWpqatKCBQv6flZUVKQ5c+Zo48aNx/1/uru71dbW1u8CABj5BnQANTU1SZLKy8v7/by8vLzvuo+qq6tTUVFR36W6unoglwQAGKK8vwtuxYoVam1t7bvs3297CykAYHga0AFUUVEhSWpubu738+bm5r7rPioej6uwsLDfBQAw8g3oAJowYYIqKiq0bt26vp+1tbVp8+bNqqmpGchfBQAY5szvguvo6NDu3bv7/t3Q0KBt27appKRE48aN05133ql/+Id/0DnnnKMJEybo29/+tqqqqnTttdcO5LoBAMOceQBt2bJFV111Vd+/ly9fLklatmyZVq9era997WtKJBK65ZZb1NLSos985jN67rnnlJ1tjJJpbFBP1C1qIxS4n8jFZcv7SCU6nGsbf2f7rFM84hY1JElNKdvJ6sFW93iVdYcPmnrvC59tqp/92c8716be+LWp96//82nn2vPPm2nqfdHFn3GuTad7TL1TmYipfs9e9w9zRyLu8TeSFIu6PyZ6e21RVmG518ez3KN1JCmW5f6YKB5VbOr9/17zX0318y5zv2+dU11m6t3c7P74HFNSYOodd4w7k6TAsOs7HI+b5gE0d+5cBZ+wklAopPvuu0/33XeftTUA4FPE+7vgAACfTgwgAIAXDCAAgBcMIACAFwwgAIAXDCAAgBcMIACAFwwgAIAXDCAAgBcMIACAF+YonjOl893fSxG3+djdnXLuW2z8uoc8S4bdIVNrlVZPdq7d1d5l6v3vb+51rn0z6Z4HJUlnTb/IVH+kJ9+59pKL5pl6t7cc/4sOjyeVtuWY5RdUOdfmZOeYeh892m6qD8K/da5NptxzACUpK+KeSxcObFmK8Sz3+9aFM8439T5y9H3n2rQxZ660rMhUf2D/751rU73dpt7xeMK59mire3blB9z3ZzqTdq7t7Ox0quMMCADgBQMIAOAFAwgA4AUDCADgBQMIAOAFAwgA4AUDCADgBQMIAOAFAwgA4AUDCADgxZCN4inKLlCuY0RIazjp3LczsMWxJOVeH+TZ4j52HD3oXPsf2/eaev9mn3scS9FZ00y99za3mer3v/Oqc+3ci6aaep8zabpz7a83bzT1fuONd51r/+tnrzb1nnWJezyRJL225dfOtYmE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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "plt.imshow(x_test[index])"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "labels[y_test[index][0]]"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "O32stbnNkTLh",
        "outputId": "c6dc9b40-290d-4029-e626-da88efc6e4e3"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Horse'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 32
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Let us make a dictionary to see how many predictions belong to each class"
      ],
      "metadata": {
        "id": "qT00-He7pfNi"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "aggregate_results = dict()\n",
        "for i in range(len(predictions)):\n",
        "  if predictions[i] in aggregate_results:\n",
        "    aggregate_results[predictions[i]] += 1\n",
        "  else:\n",
        "    aggregate_results[predictions[i]] = 1"
      ],
      "metadata": {
        "id": "x744tmPnowMr"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "aggregate_results"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "evQnjvDdqbuG",
        "outputId": "69c6d8d6-7986-444b-c66b-76c2fda98553"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'Dog': 641,\n",
              " 'Automobile': 3387,\n",
              " 'Deer': 793,\n",
              " 'Horse': 1030,\n",
              " 'Truck': 392,\n",
              " 'Frog': 290,\n",
              " 'Airplane': 179,\n",
              " 'Cat': 3175,\n",
              " 'Bird': 91,\n",
              " 'Ship': 22}"
            ]
          },
          "metadata": {},
          "execution_count": 37
        }
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
